package edu.bu.manateem.msg.heatmap;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.util.Arrays;

import org.apache.commons.collections.primitives.ArrayFloatList;
import org.apache.commons.collections.primitives.ArrayIntList;

public class ReducedIntensityPlot 
{
	private static final FireColorMap COLOR_MAP = new FireColorMap(); 

	public static BufferedImage generateImage(float threshold, ArrayIntList xValues, ArrayIntList yValues, ArrayFloatList zValues)
	{
		int width = xValues.get(xValues.size() - 1) + 1;
		int height = yValues.get(yValues.size() - 1) + 1;

		// TYPE_USHORT_GRAY does not seem to save reliably!
		int type = BufferedImage.TYPE_INT_RGB;
		BufferedImage image = new BufferedImage(width, height, type);
		Graphics gfx = image.getGraphics();
		gfx.setColor(Color.WHITE);
		gfx.fillRect(0, 0, width, height);

		plot(image, threshold, xValues, yValues,  zValues);
		
		return image;
	}


	public static void plot(BufferedImage image, float threshold, ArrayIntList xValues, ArrayIntList yValues, ArrayFloatList zValues)
	{
		int width = image.getWidth();
		int height = image.getHeight();
		WritableRaster raster = image.getRaster();
		int maxColor = image.getType() == BufferedImage.TYPE_USHORT_GRAY ? (1 << 16) - 1 : (1 << 8) - 1;

		float[]minMax = getMinMax(zValues);
		float minZ = minMax[0];
		float maxZ = minMax[1];

		threshold = Math.max(Math.max(1, threshold), minZ);
		maxZ = Math.max(threshold + 1, maxZ);

		double scale = 1.0 / Math.log(1 + maxZ - threshold);

		float gray;
		int length = xValues.size();
		int xcurrent = width;
		float[] samples = new float[height];
		for (int i = 0; i < length; i++)
		{
			float intensity = zValues.get(i);
			int xval = xValues.get(i);
			int yval = yValues.get(i);
			if (yval < 0 || yval >= height)
				continue;

			if (intensity <= threshold)
			{
				gray = 0.0F;
			}
			else
			{
				gray = (float)(Math.log(1 + intensity - threshold) * scale);
				gray = bound(gray);
			}

			if (xval != xcurrent)
			{
				if (xcurrent < width)
				{
					plotColumn(raster, xcurrent, height, samples, COLOR_MAP, maxColor);
				}
				Arrays.fill(samples, maxColor);
				xcurrent = xval;
			}
			int y = height - 1 - yval;
			samples[y] = Math.min(samples[y], maxColor - gray * maxColor);
		}
		if (xcurrent < width)
		{
			plotColumn(raster, xcurrent, height, samples, COLOR_MAP, maxColor);
		}
	}


	private static void plotColumn(WritableRaster raster, int xcurrent, int height, float[] samples, FireColorMap scheme, int maxColor)
	{
		int bands = raster.getNumBands();
		float colors[][] = scheme.remap(samples, maxColor);

		for (int i=0; i < bands && i < colors.length; i++)
		{
			raster.setSamples(xcurrent, 0, 1, height, i, colors[i]);
		}
	}

	/**
	 * Returns the minimum value in 0th index, maximum index in 1st index
	 * 
	 * @param values the list of values
	 * @return
	 */
	private static float[] getMinMax(ArrayFloatList values)
	{
		float[] result = new float[2];

		float min = Float.MAX_VALUE;
		float max = Float.MIN_VALUE;

		for (int i = 0, n = values.size(); i < n; i++)
		{
			float value = values.get(i);
			if (value > max)
			{
				max = value;
			}
			if (value < min)
			{
				min = value;
			}
		}
		result[0] = min;
		result[1] = max;

		return result;
	}

	private static float bound(float x)
	{
		return x > 1.0 ? 1.0F : x < 0.0 ? 0.0F : x;
	}

}
